const { ccclass, property } = cc._decorator;
import wallWayControl from './wallWayControl';
import runWayControl from './runWayControl';
import flyWayControl from './flyWayControl';
import elevatorControl from './elevatorControl';
import placeWayControl from './placeWayControl';
import boxControl from './boxControl';
import WayBaseControl from './WayBaseControl';
import MyEnums from '../MyEnums';

class WorldParam {
    _active: boolean = false;
    _type: string = null;
}

@ccclass
export default class WorldNodeControl extends cc.Component {
    @property(cc.Node)
    Borders: cc.Node = null;
    @property(cc.Node)
    Abyss: cc.Node = null;
    @property(cc.Node)
    ObjectNode: cc.Node = null;
    @property(cc.Node)
    heroBody: cc.Node = null;
    @property(cc.Node)
    skillNode: cc.Node = null;

    @property(cc.String)
    rootPath: string = "Config/Way/";

    @property(cc.String)
    configId: string = "01";

    _runWayY: number = 80;
    flyWayPrefab: cc.Prefab = null;
    runWayPrefab: cc.Prefab = null;
    wallWayPrefab: cc.Prefab = null;
    placeWayPrefab: cc.Prefab = null;
    propsPrefab: Map<string, cc.Prefab> = null;
    _LevelsConfig: any = null;
    _sprites: cc.SpriteFrame[] = null;
    _speedUpNode: cc.Node = null;
    flyWays: Array<cc.Node> = new Array<cc.Node>();
    staticWays: Array<cc.Node> = new Array<cc.Node>();
    runWays: Array<cc.Node> = new Array<cc.Node>();
    props: Array<cc.Node> = new Array<cc.Node>();
    dic: Map<string, WorldParam> = new Map<string, WorldParam>();
    wayIdMap: Map<string, string> = new Map<string, string>();
    onLoad() {
        this.initWorld();
    }
    initWorld() {
        HeroHelper.getInstance().widthScene = this._LevelsConfig.Width;
        this.initBorder();
        this.initSkillNode();
        if (this._LevelsConfig.Ways?.RunWays?.length > 0)
            this.initrunWays();
        if (this._LevelsConfig.Ways?.WallWays?.length > 0)
            this.initwallWays();
        if (this._LevelsConfig.Ways?.FlyWays?.length > 0)
            this.initFlyWays();
        if (this._LevelsConfig.Props?.length > 0)
            this.initProps();
        this.heroBody.zIndex = 999;
        let _heroBody = this.heroBody.children[0].getChildByName("body");
        let _rigidBody = _heroBody.getComponent(cc.RigidBody);
        _rigidBody.allowSleep = false;
    }
    private getSprites(name: string, all: boolean): cc.SpriteFrame[] {
        let sfs: Array<cc.SpriteFrame> = new Array<cc.SpriteFrame>();
        for (let i = 0; i < this._sprites.length; i++) {
            if (all) {
                if (this._sprites[i].name == name) {
                    sfs.push(this._sprites[i]);
                    break;
                }
            }
            else {
                if (this._sprites[i].name.charAt(0) == name)
                    sfs.push(this._sprites[i]);
            }
        }
        return sfs;
    }
    private initwallWays() {
        let self = this;
        this._LevelsConfig.Ways.WallWays.forEach(element => {
            let newNode = cc.instantiate(self.wallWayPrefab);
            newNode.name = "wall";
            newNode.x = element.PositionX;
            newNode.y = element.PositionY;
            newNode.group = self.node.group;

            let sfs = self.getSprites("c", false);
            let nandu = Math.floor(Math.random() * (sfs.length));
            let control = newNode.getComponent(wallWayControl);
            control.wayId = element.Id;
            control.friction = self._LevelsConfig.Friction;
            control.restitution = self._LevelsConfig.Restitution;
            control.sprite = sfs[nandu];
            control.length = element.Length;
            control.offsetUp = element.OffsetUp;
            control.offsetDown = element.OffsetDown;
            control.maxY = self._LevelsConfig.Height;
            control.worldManager = self;
            newNode.zIndex = 1000 + Math.floor(newNode.y + newNode.x / 100);
            self.staticWays.push(newNode);
            self.wayIdMap.set(element.Id, newNode.uuid);
            self.ObjectNode.addChild(newNode);
        });
    }
    private initFlyWays() {
        this._LevelsConfig.Ways.FlyWays.forEach(element => {
            let newNode = cc.instantiate(this.flyWayPrefab);
            newNode.name = "fly";
            newNode.x = element.PositionX;
            newNode.y = element.PositionY;
            newNode.group = this.node.group;

            let sfs = this.getSprites("b", false);
            let control = newNode.getComponent(flyWayControl);
            control.wayId = element.Id;
            control.friction = this._LevelsConfig.Friction;
            control.restitution = this._LevelsConfig.Restitution;
            control.beginSprite = sfs.shift();
            control.endSprite = sfs.pop();
            control.midSprite = sfs;
            control.length = element.Length;
            control.offsetUp = element.OffsetUp;
            control.offsetDown = element.OffsetDown;
            control.speed = element.Speed;
            control.maxY = this._LevelsConfig.Height;
            control.worldManager = this;
            newNode.zIndex = 1000 + Math.floor(newNode.y + newNode.x / 100);
            this.flyWays.push(newNode);
            this.wayIdMap.set(element.Id, newNode.uuid);
            this.ObjectNode.addChild(newNode);
        });
    }
    private initrunWays() {
        this._LevelsConfig.Ways.RunWays.forEach(element => {
            let newNode = cc.instantiate(this.runWayPrefab);
            newNode.name = "run";
            newNode.x = element.PositionX;
            newNode.y = this._runWayY;
            newNode.group = this.node.group;

            let control = newNode.getComponent(runWayControl);
            control.wayId = element.Id;
            control.friction = this._LevelsConfig.Friction;
            control.restitution = this._LevelsConfig.Restitution;
            let sfs = this.getSprites("a", false);
            control.beginSprite = sfs.shift();
            control.endSprite = sfs.pop();
            control.midSprite = sfs;
            control.length = element.Length;
            control.offsetUp = element.OffsetUp;
            control.worldManager = this;
            newNode.zIndex = -2000 + Math.floor(newNode.y + newNode.x / 100);
            this.runWays.push(newNode);
            this.wayIdMap.set(element.Id, newNode.uuid);
            this.ObjectNode.addChild(newNode);
        });
    }
    private initSkillNode() {
        let placeWayNode = this.skillNode.getChildByName("placeWay");
        placeWayNode.getComponent(placeWayControl).placeItemPrefab = this.placeWayPrefab;
    }
    private initProps() {
        this._LevelsConfig.Props.forEach(element => {
            if (element.Type == MyEnums.propType.box) {
                this.setBox(element);
            }
            else if (element.Type == MyEnums.propType.elevator) {
                this.setElevator(element);
            }
        });
    }
    private setBox(prop: any) {
        let name = MyEnums.propType[prop.Type];
        let newNode = cc.instantiate(this.propsPrefab.get(name));
        newNode.name = name;
        let sprite = this.getSprites(prop.Sprite, true);
        if (!sprite)
            return;
        newNode.getComponent(cc.Sprite).spriteFrame = sprite[0];
        let wayNode: cc.Node = null
        let way = this.getChildByName(prop.Way);
        if (way) {
            wayNode = way.children[prop.Index];
        }
        if (wayNode) {
            let wayControl = way.getComponent(WayBaseControl);
            let control = newNode.getComponent(boxControl);
            control.wayNode = way;
            control.offsetUp = prop.OffsetUp;
            let wayPosition = wayNode.convertToWorldSpaceAR(new cc.Vec2(0, 0));
            wayPosition.y += Math.ceil((newNode.height + wayNode.height) / 2);
            newNode.setPosition(this.ObjectNode.convertToNodeSpaceAR(wayPosition));
            newNode.zIndex = 1000 + Math.floor(newNode.x / 100);
            newNode.group = this.node.group;
            wayControl.addBoxOccupy(newNode.uuid);
            this.ObjectNode.addChild(newNode);
        }
    }
    private setElevator(prop: any) {
        let name = MyEnums.propType[prop.Type];
        let newNode = cc.instantiate(this.propsPrefab.get(name));
        newNode.name = name;
        let sprite = this.getSprites(prop.Sprite, true);
        if (!sprite)
            return;
        let wayNode: cc.Node = null
        let way = this.getChildByName(prop.Way);
        if (way) {
            wayNode = way.children[prop.Index];
        }
        if (wayNode) {
            let wayControl = way.getComponent(WayBaseControl);
            wayControl.setOccupy(prop.Index, true);
            let control = newNode.getComponent(elevatorControl);
            control.index = prop.Index;
            control.offsetUp = prop.OffsetUp;
            control.maxHeight = this._LevelsConfig.Height;
            control.stepSprite = sprite[0];
            let wayPosition = wayNode.convertToWorldSpaceAR(new cc.Vec2(0, 0));
            wayPosition.y += Math.ceil(wayNode.height / 2) - 30;
            newNode.setPosition(this.ObjectNode.convertToNodeSpaceAR(wayPosition));
            newNode.zIndex = 1000 + Math.floor(newNode.x / 100);
            newNode.group = this.node.group;
            this.ObjectNode.addChild(newNode);
        }
    }
    private getChildByName(wayId: string): cc.Node {
        let _uuid = this.wayIdMap.get(wayId);
        return this.ObjectNode.getChildByUuid(_uuid);
    }
    private getChildByUuid(ways: Array<cc.Node>, uuid: string): cc.Node {
        let node = null;
        ways.some((wayNode) => {
            if (wayNode.uuid == uuid) {
                node = wayNode;
                return true;
            }
        });
        return node;
    }
    private initBorder() {
        let borderX = this._LevelsConfig.Width / 2;
        let spriteLeftBorder = this.getSprites("BorderL", true);
        if (spriteLeftBorder.length > 0)
            this.SetBorder(spriteLeftBorder[0], 0 - borderX);
        let spriteRightBorder = this.getSprites("BorderR", true);
        if (spriteRightBorder.length > 0)
            this.SetBorder(spriteRightBorder[0], borderX);

        let abyss = this.Abyss.addComponent(cc.PhysicsBoxCollider);
        abyss.size.width = this._LevelsConfig.Width;
        abyss.size.height = 10;
        abyss.tag = TagAbyss;
        abyss.apply();
    }
    private SetBorder(spriteF: cc.SpriteFrame, borderX: number) {
        let Border = new cc.Node();
        Border.group = this.Borders.group;
        Border.anchorY = 0;
        if (borderX > 0)
            Border.anchorX = 1;
        else
            Border.anchorX = 0;
        let sprite = Border.addComponent(cc.Sprite);
        sprite.spriteFrame = spriteF;
        sprite.type = cc.Sprite.Type.SIMPLE;
        sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
        Border.height = this._LevelsConfig.Height;
        Border.x = borderX;

        let body = Border.addComponent(cc.RigidBody);
        body.type = cc.RigidBodyType.Static;
        let collider = Border.addComponent(cc.PhysicsBoxCollider);
        collider.friction = 0;
        collider.restitution = 0.2;
        collider.size = new cc.Size(Border.width, 1000);
        collider.tag = TagBorder;
        if (borderX < 0)
            collider.offset.x = Border.width / 2;
        else
            collider.offset.x = 0 - Border.width / 2;
        collider.offset.y = Border.height / 2;
        Border.parent = this.Borders;
    }
    speedUp(uuid: string, speed: number, time: number) {
        let node = this.getChildByUuid(this.flyWays, uuid);
        if (!this._speedUpNode) {
            let sf = this.getSprites("speedUp", true);
            this._speedUpNode = new cc.Node();

            let sprite = this._speedUpNode.addComponent(cc.Sprite);
            sprite.spriteFrame = sf[0];
        }
        if (node) {
            this._speedUpNode.zIndex = 1;
            let flyControl = node.getComponent(flyWayControl);
            if (!flyControl)
                return;
            this._speedUpNode.active = true;
            this._speedUpNode.opacity = 0;
            let faceTo = 1;
            if (speed < 0) {
                faceTo = -1;
            }
            this._speedUpNode.scaleX = faceTo;

            this._speedUpNode.x = (node.width - this._speedUpNode.width + 30) / 2 * faceTo;
            this._speedUpNode.parent = node;
            let body = node.getComponent(cc.RigidBody);

            let lv = body.linearVelocity;
            lv.x += speed;
            body.linearVelocity = lv;
            cc.tween(this._speedUpNode)
                .to(time / 2, { opacity: 255 })
                .to(time / 2, { opacity: 0 })
                .call(() => {
                    this._speedUpNode.active = false;
                    lv.x = flyControl.speed * faceTo;
                    body.linearVelocity = lv;
                })
                .start()
        }
    }
    //获取场景内的Ways
    GetWaysByActive(active: boolean, wayType: string): Array<any> {
        let ways = Array<any>();
        this.dic.forEach((value, key) => {
            if (value._active == active) {
                if (!wayType)
                    ways.push({ type: value._type, id: key });
                else if (wayType == value._type)
                    ways.push({ type: value._type, id: key });
            }
        });
        return ways;
    }
    //设置是否在场景中
    SetWayActive(active: boolean, uuid: string, type: string) {
        let p = new WorldParam();
        p._active = active;
        p._type = type;
        this.dic.set(uuid, p);
    }
}
